﻿/****************************************************************************************************
	Copyright (C) 2010 RapidWebDev Organization (http://rapidwebdev.org)
	Author: Eunge, Legal Name: Jian Liu, Email: eunge.liu@RapidWebDev.org

	This program is free software: you can redistribute it and/or modify
	it under the terms of the GNU General Public License as published by
	the Free Software Foundation, either version 2 of the License, or
	(at your option) any later version.

	This program is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
	GNU General Public License for more details.

	You should have received a copy of the GNU General Public License
	along with this program.  If not, see <http://www.gnu.org/licenses/>.
 ****************************************************************************************************/

using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Runtime.Serialization;
using Lucene.Net.Search;
using RapidWebDev.SearchIndex.Contract;
using Lucene.Net.Documents;

namespace RapidWebDev.SearchIndex
{
	internal class SearchAggregateApi
	{
		internal static Collection<AggregateResultMessage> Analyze(string applicationId, IndexSearcher searcher, TopFieldDocs topFieldDocs, IEnumerable<SearchWithAggregateFieldMessage> aggregateParameters)
		{
			Collection<AggregateResultMessage> results = new Collection<AggregateResultMessage>();
			if (aggregateParameters == null || aggregateParameters.Count() == 0)
				return results;

			Dictionary<string, string> aggregateFieldNames = aggregateParameters.Select(p => p.FieldName).Distinct()
				.ToDictionary(fieldName => fieldName, fieldName => LuceneFields.FieldType(fieldName));
			string[] neededLuceneFields = aggregateFieldNames.Keys.Concat(aggregateFieldNames.Values).Distinct().ToArray();

			// <FieldName, <Keyword, Hit Number>>
			Dictionary<string, Dictionary<string, int>> aggregateResults = new Dictionary<string, Dictionary<string, int>>(StringComparer.OrdinalIgnoreCase);
			for (int i = 0; i < topFieldDocs.scoreDocs.Length; i++)
			{
				Document doc = searcher.Doc(topFieldDocs.scoreDocs[i].doc, new MapFieldSelector(neededLuceneFields));
				foreach (string aggregateFieldName in aggregateFieldNames.Keys)
				{
					string fieldTypeFieldName = aggregateFieldNames[aggregateFieldName];
					string fieldTypeText = doc.Get(fieldTypeFieldName);
					try
					{
						IndexDocFieldValueTypes fieldType = (IndexDocFieldValueTypes)Enum.Parse(typeof(IndexDocFieldValueTypes), fieldTypeText);

						// only support aggregation on Keyword fields now.
						if (fieldType == IndexDocFieldValueTypes.Keyword)
						{
							if (!aggregateResults.ContainsKey(aggregateFieldName))
								aggregateResults[aggregateFieldName] = new Dictionary<string, int>(StringComparer.OrdinalIgnoreCase);

							// get field values
							IEnumerable<string> fieldValues = doc.GetValues(aggregateFieldName).Distinct();
							foreach (string fieldValue in fieldValues)
							{
								if (!aggregateResults[aggregateFieldName].ContainsKey(fieldValue))
									aggregateResults[aggregateFieldName][fieldValue] = 1;
								else
									aggregateResults[aggregateFieldName][fieldValue]++;
							}
						}
					}
					catch
					{
					}
				}
			}

			foreach (string aggregateFieldName in aggregateResults.Keys)
			{
				AggregateResultMessage aggregateResult = new AggregateResultMessage
				{
					FieldName = aggregateFieldName,
					ApplicationId = applicationId,
					Values = new Collection<AggregateFieldResultMessage>((from kvp in aggregateResults[aggregateFieldName]
																		  orderby kvp.Value descending
																		  select new AggregateFieldResultMessage
																		  {
																			  Value = kvp.Key,
																			  ApproximateTotalHits = kvp.Value
																		  }).ToList())
				};

				results.Add(aggregateResult);
			}

			return results;
		}
	}
}